lib/gpg: Don't kill gpg-agent on newer gnupg
authorDan Nicholson <nicholson@endlessm.com>
Thu, 5 Sep 2019 18:20:04 +0000 (12:20 -0600)
committerAtomic Bot <atomic-devel@projectatomic.io>
Fri, 6 Sep 2019 18:04:05 +0000 (18:04 +0000)
GnuPG 2.1.17 contains a bug fix so that `gpg-agent` is killed when the
entire GPG home directory is deleted[1]. If the host's GnuPG is new
enough, then we don't need to bother calling `gpg-connect-agent` to kill
the agent since it will be cleaned up on its own.

Get the GnuPG version from the GPGME OpenPGP engine info and parse it to
see if it matches this criteria.

1. https://dev.gnupg.org/T2756

Closes: #1915
Approved by: cgwalters

src/libotutil/ot-gpg-utils.c

index 35e854b32f86b71a7bd156b1a9eb077858b149a7..97a1c756ce3b9f1cf21cb78fe5d50580b97e3514 100644 (file)
@@ -439,14 +439,79 @@ ot_gpgme_new_ctx (const char *homedir,
   return g_steal_pointer (&context);
 }
 
+static gboolean
+get_gnupg_version (guint *major,
+                   guint *minor,
+                   guint *patch)
+{
+  g_return_val_if_fail (major != NULL, FALSE);
+  g_return_val_if_fail (minor != NULL, FALSE);
+  g_return_val_if_fail (patch != NULL, FALSE);
+
+  gpgme_engine_info_t info;
+  gpgme_error_t err = gpgme_get_engine_info (&info);
+  if (err != GPG_ERR_NO_ERROR)
+    {
+      g_debug ("Failed to get GPGME engine info: %s: %s",
+               gpgme_strsource (err), gpgme_strerror (err));
+      return FALSE;
+    }
+
+  const char *gnupg_version = NULL;
+  for (; info != NULL; info = info->next)
+    {
+      if (info->protocol == GPGME_PROTOCOL_OpenPGP)
+        {
+          gnupg_version = info->version;
+          break;
+        }
+    }
+
+  if (gnupg_version == NULL)
+    {
+      g_debug ("Could not determine GnuPG version");
+      return FALSE;
+    }
+
+  g_auto(GStrv) parts = g_strsplit (gnupg_version, ".", 4);
+  if (g_strv_length (parts) < 3)
+    {
+      g_debug ("Less than 3 components in GnuPG version \"%s\"", gnupg_version);
+      return FALSE;
+    }
+
+  *major = g_ascii_strtoull (parts[0], NULL, 10);
+  *minor = g_ascii_strtoull (parts[1], NULL, 10);
+  *patch = g_ascii_strtoull (parts[2], NULL, 10);
+
+  return TRUE;
+}
+
 void
 ot_gpgme_kill_agent (const char *homedir)
 {
   g_return_if_fail (homedir != NULL);
 
+  /* If gnupg is at least 2.1.17, gpg-agent will exit when the homedir
+   * is deleted.
+   */
+  guint gnupg_major = 0, gnupg_minor = 0, gnupg_patch = 0;
+  if (get_gnupg_version (&gnupg_major, &gnupg_minor, &gnupg_patch))
+    {
+      if ((gnupg_major > 2) ||
+          (gnupg_major == 2 && gnupg_minor > 1) ||
+          (gnupg_major == 2 && gnupg_minor == 1 && gnupg_patch >= 17))
+        {
+          /* Note early return */
+          g_debug ("GnuPG >= 2.1.17, skipping gpg-agent cleanup in %s", homedir);
+          return;
+        }
+    }
+
   /* Run gpg-connect-agent killagent /bye */
   g_autoptr(GError) local_error = NULL;
   GSubprocessFlags flags = G_SUBPROCESS_FLAGS_STDOUT_SILENCE | G_SUBPROCESS_FLAGS_STDERR_PIPE;
+  g_debug ("Killing gpg-agent in %s", homedir);
   g_autoptr(GSubprocess) proc = g_subprocess_new (flags,
                                                   &local_error,
                                                   "gpg-connect-agent",